package com.chaosj.platform.wx.runner;

import cn.hutool.core.lang.ClassScanner;
import cn.hutool.core.text.StrFormatter;
import com.chaosj.memory.command.AbstractCommand;
import com.chaosj.tools.SpringUtils;
import com.google.common.base.Joiner;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.shell.standard.ShellMethod;
import org.springframework.stereotype.Service;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @className: SpringShellRunner
 * @description:
 * @author: caoyangjie
 * @date: 2022/3/22
 **/
@Service
public class SpringShellRunner implements CommandLineRunner, ApplicationContextAware {
    private ApplicationContext applicationContext;
    private List<CallMethod> methods = new ArrayList<>();

    @Override
    public void run(String... args) throws Exception {
        ClassScanner.scanPackageBySuper("com.chaosj", AbstractCommand.class).forEach(cls->{
            ReflectionUtils.doWithMethods(cls, method -> {
                AbstractCommand command = getCommand(cls);
                String methodName = method.getName();
                methods.add(new CallMethod(methodName, command, method));
            },mf-> mf.getAnnotation(ShellMethod.class)!=null);
        });
    }

    protected AbstractCommand getCommand(Class clazz){
        return (AbstractCommand) Optional.ofNullable(applicationContext.getBean(clazz)).orElse(BeanUtils.instantiateClass(clazz));
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    public String call(List<String> args) {
        return methods.stream().filter(method->method.filter(args)).findAny().orElse(new CallMethod("",null,null){
            @Override
            public String invoke(List<String> args) {
                return StrFormatter.format("未能找到指令[{}]执行集!", Joiner.on(" ").join(args));
            }
        }).invoke(args);
    }

    @Slf4j
    private static class CallMethod {
        private String methodName;
        private Object instance;
        private Method realMethod;

        public CallMethod(String methodName, Object instance, Method realMethod) {
            this.methodName = methodName;
            this.instance = instance;
            this.realMethod = realMethod;
        }

        public String invoke(List<String> args){
            try {
                return Optional.ofNullable(realMethod.invoke(instance, args.subList(1,args.size()).toArray())).orElse(StrFormatter.format("执行指令 [{}] 结果为空!", args.stream().collect(Collectors.joining(" ")))).toString();
            } catch (IllegalAccessException e) {
                log.error(e.getMessage());
            } catch (InvocationTargetException e) {
                log.error(e.getMessage());
            }
            return StrFormatter.format( "执行指令 [{}] 失败!", args.stream().collect(Collectors.joining(" ")));
        }

        public boolean filter(List<String> args) {
            Type[] types = realMethod.getGenericParameterTypes();
            return methodName.equalsIgnoreCase(args.get(0)) && types.length == args.size() - 1;
        }
    }
}
